﻿/*
 * ************************************************
 * Clase Arduino en C# para control de un arduino *
 * que tenga cargado el Firmware FIRMA_SQCNC.     *
 *                                                *
 * La Clase Arduino.cs y FIRMA_SQCNC.pde han sido *
 * creados por Hazael Fernando Mojica Garcia      *
 * como parte del Proyecto SQUARE CNC             *
 *                                                * 
 * Monterrey, Mexico 2011                         *
 * ************************************************
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;


namespace SqArduino
{
    public class Arduino
    {
        private String nompuerto = ""; //Propiedad que contiene el nombre del puerto serie
        public SerialPort sp;          //Creamos un objeto de la clase SerialPort
        private bool inicializado = false;//variable que indica si ya se inicializo la comunicacion o no

        public Pin pin;

        //Constructor de la clase Arduino
        //construye la comunicacion serial con un 115200 bauds el estandar para SquareCNC
        //y pasando de parametro un String con el nombre del puerto
        public Arduino(String nombrePuerto)
        {
            nompuerto = nombrePuerto;
            NombrePuerto = nombrePuerto;
            pin = new Pin();//creamos un objeto Pin

            sp = new SerialPort(nombrePuerto, 115200);

        }

        //Metodo publico, manda la instruccion digitalWrite al arduino
        //la instruccion solo se mandara cuando el arduino se encuetre inicializado
        //estado puede ser HIGH o LOW 
        public void digitalWrite(int pin, String estado)
        {
            if (inicializado)
            {
                sp.Write("dW" + "," + pin.ToString() + "," + estado + ",%");
            }
        }

        //Metodo publico que manda la instruccion analogWrite al arduino
        //la instruccion solo se mandara cuando el arduino se encuentre inicializado
        //bits que puede ser de 0 a 255
        public void analogWrite(int pin, int bits)
        {
            if (inicializado)
            {
                sp.Write("aW" + "," + pin.ToString() + "," + bits.ToString() + ",%");
            }
        }


        //Metodo publico que manda la instruccion digitalRead al arduino
        //Regresa HIGH o LOW o "" si la comunicacion no esta inicializada
  
        public String digitalRead(int pin)
        {
            String cadena="";
            if (inicializado)
            {
                sp.Write("dR" +"," + pin.ToString() + "," +"0" + ",%");
                cadena = recibe_cadena();
            }
            return cadena;
        }

        //Metodo publico que envia al arduino la instruccion analogRead
        //regresa un entero que contiene los bits leidos por el convertidor 0->1023
        //se pasa de parametro un entero con el pin analogo correspondiente a leer
        //0 a 7 en la mayoria de los arduinos, 0 a 15 en el Mega
        public int analogRead(int pin)
        {
            String cadena;
            if (inicializado)
            {
                sp.Write("aR" + "," + pin.ToString() + "," + "0" + ",%");
                cadena = recibe_cadena();
            }
            else
            {
                cadena = "0";
            }

            return Convert.ToInt32(Convert.ToChar(cadena));
        }


        //metodo publico que manda al arduino la instruccion pinMode
        //pasando de parametro el pin y el modo
        //solo mandara la instruccion si el arduino se encuentra inicializado
        public void pinMode(int pin,String modo)
        {
            if (inicializado)
            {
                sp.Write("pM" + "," + pin.ToString() + "," + modo + ",%");
            }
        }



        //Metodo publico que coloca al arduino en a la espera de una nueva
        //inicilizacion, puede usarlo para cambiar de puerto serie
        public void finaliza_comunicacion()
        {
            sp.Write("FIN%");
            sp.Close();
            inicializado = false;
        }

        //metodo privado, lee una cadena enviada por el arduino
        //cada pakete de bytes recibidos terminara con '%'
        private String recibe_cadena()
        {
            String cadena = "";
            char caracter;
            long i = 0;
            bool verif = true;

            while (sp.BytesToRead <= 0)
            {
                //esperamos un tiempo a que manden la cadena
                if (i > 999)
                {
                    verif = false;
                    break;
                }
                i++;
            }

            i = 0;

            while (true && verif == true)
            {
                caracter = Convert.ToChar(sp.ReadByte());
                if (caracter != '%')
                {
                    cadena += caracter;
                }
                else
                {
                    break;
                }

                i++;
            }

            return cadena;
        }


        //metodo publico, este inicializa la comunicacion con el arduino
        public bool inicializa_comunicacion()
        {
            long i = 0;
            bool arduino_verif = true;
            String cadena = "";

            //Intentamos abrir el puerto
            if (!sp.IsOpen)
            {
                sp.Open();
            }
            else
            {
              //  MessageBox.Show(null, "El puerto " + nompuerto + " se encuentra en uso por otra aplicacion", "ERROR, el puerto seleccionado esta en uso", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false; //terminamos la ejecucion del metodo
            }

            //enviamos la primera cadena de contacto
            sp.WriteLine("FIRMA_SQCNC%");

            while (sp.BytesToRead <= 0)
            {
                //esperamos a que mande la cadena de verificacion de conexion
                if (i > 999)
                {
                    arduino_verif = false;//termino el tiempo de espera
                    break;
                }
                i++;
            }

            if (arduino_verif)//si existe algo en el buffer de lectura
            {
                cadena = sp.ReadLine();//si hay algo en el buffer de lectura, lo leemos
                cadena = cadena.Substring(0, cadena.Length - 1);//quitamos el salto de linea al final
                if (cadena == "FIRMA_SQCNC")
                {
                    sp.Write("ok%");//si es la cadena de verificacion correcta entonces confirmamos la comunicacions serie mandando el bloque de confirmacion "ok%"
                    inicializado = true;
                }
                else
                {
                    arduino_verif = false;
                }
            }

            if (!arduino_verif)
            {
              //  MessageBox.Show(null, "ERROR, el dispositivo no responde de la manera correcta, es posible que el puerto selccionado no sea el correcto o el arduino no tiene cargado el FIRMA_SQCNC", "ERROR de Comunicacion", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return arduino_verif;
        }



        //Propiedad modificable
        //contiene el nombre del puerto en el cual esta colocado el Arduino
        public String NombrePuerto
        {
            get
            {
                return nompuerto;
            }
            set
            {
                nompuerto = value;
            }


        }

        //propiedad de solo lectura, regresa un bool que indica si el arduino se encuentra en estado inicializado
        public bool Inicializado
        {
            get
            {
                return inicializado;
            }
        }
    }//Termina la clase Arduino

    //creamos una subclase llamada Pin para llevar el control de los estados de los pines del arduino
    public class Pin
    {
        String modoOUTPUT = "O";
        String modoINPUT = "I";
        String estadoHIGH = "H";
        String estadoLOW = "L";

        public Pin()
        {
            //Constructor vacio, no necesitamso inicializar nada para esta clase
        }
        //Colocamos todas las propiedades publicas de solo lectura
        public String ModoOUTPUT
        {
            get
            {
                return modoOUTPUT;
            }
        }

        public String ModoINPUT
        {
            get
            {
                return modoINPUT;
            }
        }

        public String EstadoHIGH
        {
            get
            {
                return estadoHIGH;
            }
        }

        public String EstadoLOW
        {
            get
            {
                return estadoLOW;
            }
        }
    }//Terminamos la clase Pin

}//Cerramos el namespace de tu proyecto